package utils

import (
	"errors"
	"fmt"
	"github.com/golang-jwt/jwt"
	"gvb-server/global"
	"time"
)

type MyClaims struct {
	Id       int    `json:"id"`
	Username string `json:"username"`
	Password string `json:"password"`
	jwt.StandardClaims
}

type userinfo struct {
	Id       int
	UserName string
	Password string
}

func HandleToken(username, password string) (Res Response) {
	// 将 int64 转换为 time.Duration
	var TokenExpireDuration = time.Duration(global.Config.Jwt.Expires) * time.Hour

	// 数据库验证 俩种方法都可以
	//List := models.Demo{}
	List := userinfo{}
	//pwd := HashPassword([]byte(password))

	var err error
	global.DB.Table("users").Where("user_name = ?", username).Find(&List)

	fmt.Printf("result:%#v", List)

	//我们将List初始化为userinfo结构体的零值。如果查询结果为空，即没有匹配的数据，List仍然保持为零值。因此，我们通过通过判断List是否等于userinfo{}来确定查询结果是否为空。
	//如果List不等于userinfo{}，表示查询到了数据，您可以继续进行后续操作。否则，可以根据需要处理结果为空的情况
	if List == (userinfo{}) {
		Res.Code = ERROR
		Res.Msg = "尚未认证数据"
		return
	}
	fmt.Printf("Password:%#v\n", password)

	bl := CheckPassword(List.Password, []byte(password))
	fmt.Printf("bl:%v", bl)
	if !bl {
		Res.Code = ERROR
		Res.Msg = "密码输入有误～"
		return
	}
	// 方法二 验证是否有数据
	//err = global.DB.Take(&List, "user_name = ? and password = ?", username, pwd).Error
	//if err != nil {
	//	Res.Code = ERROR
	//	Res.Msg = "尚未认证数据"
	//	return
	//}

	// 创建一个我们自己的声明。可以用new来实例这个结构体，new后是保存在堆内存中 需要手动释放内存，而直接初始化的可以自动释放内存
	// make 只能用于 map 、slice 、channel ，new 只能用于结构体
	// new(T) 会为 T 类型的新值分配已置零的内存空间，并返回地址（指针），即类型为 *T的值。换句话说就是，返回一个指针，该指针指向新分配的、类型为 T 的零值。适用于值类型，如数组、结构体等。
	// make(T,args) 返回初始化之后的 T 类型的值，这个值并不是 T 类型的零值，也不是指针 *T，是经过初始化之后的 T 的引用。make() 只适用于 slice、map 和 channel.

	//返回类型不同：直接初始化结构体变量返回的是结构体变量本身，而使用 new 函数创建结构体变量返回的是该结构体变量的指针。
	//内存分配位置不同：直接初始化结构体变量时，变量的值会被分配在栈内存中，在作用域结束后，该内存会自动释放；而使用 new 函数创建结构体变量时，变量的值会被分配在堆内存中，需要手动释放内存避免造成内存泄漏。
	//初始化方式不同：直接初始化结构体变量时，需要按字段定义的顺序逐一为每个字段赋值；而使用 new 函数创建结构体变量时，可以使用结构体字面量或者逐个赋值的方式来初始化每个字段。
	//使用 new 函数创建结构体变量可以确保所有字段都被初始化为默认值，而直接初始化结构体变量时需要手动初始化每个字段。
	info := MyClaims{
		List.Id,
		username, // 自定义字段
		password,
		jwt.StandardClaims{
			ExpiresAt: time.Now().Add(TokenExpireDuration).Unix(), // 过期时间
			Issuer:    "my-project",                               // 签发人
		},
	}
	// 使用指定的签名方法创建签名对象
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, info)
	// 使用指定的secret签名并获得完整的编码后的字符串token
	var tmp string
	tmp, err = token.SignedString(MYSECRET)

	if err != nil {
		Res.Code = ERROR
		Res.Msg = "token  失败"
		return
	}

	Res.Code = SUCCESS
	Res.Msg = ""
	Res.Data = ResDataToken{
		Id:    List.Id,
		Name:  List.UserName,
		Token: tmp,
	}
	return
}

// ParseToken 解析JWT
func ParseToken(tokenString string) (*MyClaims, error) {

	fmt.Println("=====解析token=====")
	fmt.Println(tokenString)

	// 解析token
	token, err := jwt.ParseWithClaims(tokenString, &MyClaims{}, func(token *jwt.Token) (i interface{}, err error) {
		return MYSECRET, nil
	})
	if err != nil {
		return nil, err
	}
	if claims, ok := token.Claims.(*MyClaims); ok && token.Valid { // 校验token
		return claims, nil
	}
	return nil, errors.New("invalid token")
}
